// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

#include <VTKPluginInfo.h>
#include <avtVTKFileFormat.h>
#include <avtSTMDFileFormatInterface.h>
#include <avtGenericDatabase.h>
#include <avtVTKOptions.h>

VTKCommonPluginInfo::VTKCommonPluginInfo() : CommonDatabasePluginInfo(), VTKGeneralPluginInfo()
{
    // Assume SD by default.
    dbType = DB_TYPE_STSD;
}

// ****************************************************************************
//  Method:  VTKCommonPluginInfo::GetDatabaseType
//
//  Purpose:
//    Returns the type of a VTK database.
//
//  Programmer:  generated by xml2info
//  Creation:    omitted
//
// ****************************************************************************
DatabaseType
VTKCommonPluginInfo::GetDatabaseType()
{
    return dbType;
}


// ****************************************************************************
//  Method: VTKCommonPluginInfo::SetupDatabase
//
//  Purpose:
//      Sets up a VTK database.
//
//  Arguments:
//      list    A list of file names.
//      nList   The number of timesteps in list.
//      nBlocks The number of blocks in the list.
//
//  Returns:    A VTK database from list.
//
//  Programmer: generated by xml2info
//  Creation:   omitted
//
//  Modifications:
//    Brad Whitlock, Mon Oct 22 17:03:44 PDT 2012
//    I changed the code so it switches the interface based on how many domains
//    are present in the 1st file.
//
//    Kathleen Biagas, Fri August 13, 2021
//    Add MTMD for .pvd file types.
//
//    Kathleen Biagas, Thu Dec 12, 2024 
//    Added tests to see if .pvd is GEOS flavor.
//
// ****************************************************************************

#include <FileFunctions.h>
#include <vtkNew.h>
#include <vtkXMLDataElement.h>
#include <vtkXMLDataParser.h>
using std::string;

bool
FindGEOSNestedElement(vtkXMLDataElement *el)
{
    if(el->GetNumberOfNestedElements() == 0)
        return false;

    // check for Blocks with certain name attributes used by GEOS
    if(el->FindNestedElementWithNameAndAttribute("Block", "name","CellElementRegion") ||
       el->FindNestedElementWithNameAndAttribute("Block", "name","SurfaceElementRegion") ||
       el->FindNestedElementWithNameAndAttribute("Block", "name","ParticleRegion") ||
       el->FindNestedElementWithNameAndAttribute("Block", "name","WellElementRegion") )
    {
        return true;
    }
    for(int i = 0; i < el->GetNumberOfNestedElements(); ++i)
        return FindGEOSNestedElement(el->GetNestedElement(i));

    return false;
}

bool
CheckIfGEOSVTM(const string &vtmFile)
{
    vtkNew<vtkXMLDataParser> vtmParser;
    vtmParser->SetFileName(vtmFile.c_str());
    if(!vtmParser->Parse())
        return false;

    vtkXMLDataElement *vtmRoot = vtmParser->GetRootElement();
    return FindGEOSNestedElement(vtmRoot);
}


bool
CheckIfGEOSPVD(const string &rootFile)
{
    vtkNew<vtkXMLDataParser> pvdParser;
    pvdParser->SetFileName(rootFile.c_str());
    if(pvdParser->Parse())
    {
        vtkXMLDataElement *root = pvdParser->GetRootElement();
        if(root)
        {
            vtkXMLDataElement *datasetNode = root->LookupElementWithName("DataSet");
            if(datasetNode) 
            {
                string fileName = datasetNode->GetAttribute("file");
                size_t pos1 = fileName.find_last_of('.');
                string ext1 = fileName.substr(pos1 + 1);
                if(ext1 == "vtm")
                {
                    // extract dir from rootFile and prepend to fileName:
                    string dir = FileFunctions::Dirname(rootFile);
                    string file = dir + VISIT_SLASH_STRING + fileName;
                    return CheckIfGEOSVTM(file);
                }
            }
        }
    }
    return false;
}


#include <avtSTSDFileFormatInterface.h>
#include <avtMTMDFileFormatInterface.h>

avtDatabase *
VTKCommonPluginInfo::SetupDatabase(const char *const *list,
                                   int nList, int nBlock)
{
    avtDatabase *db = NULL;

    string fn(list[0]);
    size_t pos = fn.find_last_of('.');
    string ext = fn.substr(pos+1);
    if (ext == "pvd")
    {
        dbType = DB_TYPE_MTMD;

        // Only using 1 timestepgroup
        avtMTMDFileFormat **ffl = new avtMTMDFileFormat*[1];
        if(CheckIfGEOSPVD(fn))
            ffl[0] = new avtGEOSFileFormat(fn.c_str(), readOptions);
        else
            ffl[0] = new avtPVD_MTMDFileFormat(fn.c_str(), readOptions);
        avtMTMDFileFormatInterface *inter = new avtMTMDFileFormatInterface(ffl, 1);
        db = new avtGenericDatabase(inter);

        return db;
    }
    else if (ext == "vtm")
    {
        if(CheckIfGEOSVTM(fn))
        {
            dbType = DB_TYPE_STMD;
            avtSTMDFileFormat **ffl = new avtSTMDFileFormat*[nList];
            avtGEOSFileReader *reader = new avtGEOSFileReader(list[0], readOptions);
            for (int i = 0; i < nList; i++)
            {
                if(i == 0)
                    ffl[i] = new avtGEOS_STMDFileFormat(list[i], readOptions, reader);
                else
                    ffl[i] = new avtGEOS_STMDFileFormat(list[i], readOptions);
            }
            avtSTMDFileFormatInterface *inter
               = new avtSTMDFileFormatInterface(ffl, nList);
            db = new avtGenericDatabase(inter);

            return db;
        }
    }


    // Figure out how many domains there are in the 1st file.
    avtVTKFileReader *reader = new avtVTKFileReader(list[0], readOptions);
    int nDomains = 1;
    TRY
    {
        nDomains = reader->GetNumberOfDomains();
    }
    CATCHALL
    {
        delete reader;
        RETHROW;
    }
    ENDTRY

    // Return the proper interface.
    if(nDomains > 1)
    {
        dbType = DB_TYPE_STMD;

        // STMD case
        avtSTMDFileFormat **ffl = new avtSTMDFileFormat*[nList];
        for (int i = 0; i < nList; i++)
        {
            if(i == 0)
                ffl[i] = new avtVTK_STMDFileFormat(list[i], readOptions, reader);
            else
                ffl[i] = new avtVTK_STMDFileFormat(list[i], readOptions);
        }
        avtSTMDFileFormatInterface *inter
           = new avtSTMDFileFormatInterface(ffl, nList);
        db = new avtGenericDatabase(inter);
    }
    else
    {
        dbType = DB_TYPE_STSD;

        // STSD case
        int nTimestep = nList / nBlock;
        avtSTSDFileFormat ***ffl = new avtSTSDFileFormat**[nTimestep];
        for (int i = 0; i < nTimestep; i++)
        {
            ffl[i] = new avtSTSDFileFormat*[nBlock];
            for (int j = 0; j < nBlock; j++)
            {
                if(i == 0 && j == 0)
                    ffl[i][j] = new avtVTK_STSDFileFormat(list[i*nBlock + j], readOptions, reader);
                else
                    ffl[i][j] = new avtVTK_STSDFileFormat(list[i*nBlock + j], readOptions);
            }
        }
        avtSTSDFileFormatInterface *inter
            = new avtSTSDFileFormatInterface(ffl, nTimestep, nBlock);
        db = new avtGenericDatabase(inter);
    }

    return db;
}


// ****************************************************************************
//  Method: VTKCommonPluginInfo::GetReadOptions
//
//  Purpose:
//      Gets the read options.
//
//  Programmer: generated by xml2info
//  Creation:   omitted
//
// ****************************************************************************

DBOptionsAttributes *
VTKCommonPluginInfo::GetReadOptions() const
{
    return GetVTKReadOptions();
}

// ****************************************************************************
//  Method: VTKCommonPluginInfo::GetWriteOptions
//
//  Purpose:
//      Gets the write options.
//
//  Programmer: generated by xml2info
//  Creation:   omitted
//
// ****************************************************************************

DBOptionsAttributes *
VTKCommonPluginInfo::GetWriteOptions() const
{
    return GetVTKWriteOptions();
}

